﻿using System;
using System.Configuration;
using Com.Pernix.Common.Log.Configs;
using Com.Pernix.Common.Log.Exceptions;
using System.Text;

namespace Com.Pernix.Common.Log
{
    public class Logger
    {
        private readonly LogWriter _logWriter;
        private readonly LogConfigurationSection _configSection;
        private readonly LogConfiguration _logConfig;
        private readonly string _logname;
        private readonly Level _logLevel;

        public Logger(Type type):this(type.ToString())
        {
        }
        public Logger(string logname)
        {
            try
            {
                _configSection = ConfigurationManager.GetSection("LogConfigurations") as LogConfigurationSection;
            }
            catch (ConfigurationErrorsException)
            {
                throw new InvalidConfigurationException(String.Format("Invalid Loging Configuration [logname='{0}']", logname));
            }
            if (_configSection != null)
            {
                var logConfiguration = _configSection.Logs[logname];
                if (logConfiguration != null)
                {
                    _logConfig = logConfiguration;
                    _logLevel = ConvertStringToLevel(_logConfig.Level);
                    _logname = _logConfig.Name;
                    _logWriter = CreateLogWriter();
                }
                else
                {
                    logConfiguration = _configSection.Logs["global"];
                    if (logConfiguration != null)
                    {
                        _logConfig = logConfiguration;
                        _logLevel = ConvertStringToLevel(_logConfig.Level);
                        _logname = logname;
                        _logWriter = CreateLogWriter();
                    }
                    else
                    {
                        throw new InvalidConfigurationException(String.Format("Invalid Global Loging Configuration [logname='{0}']", logname));
                    }
                }
            }
            else
            {
                throw new InvalidConfigurationException(String.Format("Invalid Loging Configuration [logname='{0}']", logname));
            }
        }

        private Level ConvertStringToLevel(string levelString)
        {
            try
            {
                var level = (Level)Enum.Parse(typeof(Level), levelString);
                return level;
            }
            catch (Exception)
            {
                return Level.Off;
            }
        }

        private LogWriter CreateLogWriter()
        {
            if (_logConfig != null)
            {
                return new LogWriter(_logConfig);
            }
            return null;
        }

        public virtual string GetBaseMessage(string logname, string level)
        {
            var baseMessage = new StringBuilder();
            baseMessage.AppendFormat("{0}[{1}][{2}]", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"), level, logname);
            return baseMessage.ToString();
        }

        public virtual void Info(string message)
        {
            if (_logLevel >= Level.Info)
            {
                var infoMessage = new StringBuilder();
                infoMessage.AppendFormat("{0} => {1}", GetBaseMessage(_logname, "INFO"), message);
                Write(infoMessage.ToString());
            }
        }
        public virtual void InfoFormat(string format, params object[] args)
        {
            Info(String.Format(format, args));
        }

        public virtual void Debug(string message)
        {
            if (_logLevel >= Level.Debug)
            {
                var debugMessage = new StringBuilder();
                debugMessage.AppendFormat("{0} => {1}", GetBaseMessage(_logname, "DEBUG"), message);
                Write(debugMessage.ToString()); 
            }
        }
        public virtual void DebugFormat(string format, params object[] args)
        {
            Debug(String.Format(format, args));
        }
        public virtual void Warning(string message)
        {
            if (_logLevel >= Level.Warning)
            {
                var warningMessage = new StringBuilder();
                warningMessage.AppendFormat("{0} => {1}", GetBaseMessage(_logname, "WARNING"), message);
                Write(warningMessage.ToString()); 
            }
        }
        public virtual void WarningFormat(string format, params object[] args)
        {
            Warning(String.Format(format, args));
        }
        public virtual void ErrorFormat(Exception exception, string message, params object[] args)
        {
            Error(exception, string.Format(message, args));
        }
        public virtual void Error(Exception exception, string message)
        {
            var errorMessage = new StringBuilder();
            errorMessage.AppendFormat("{0}{1}", message, Environment.NewLine);
            errorMessage.AppendFormat("=> {0}{1}{2}", "Exception:", exception.Message, Environment.NewLine);
            errorMessage.AppendFormat("=> {0}{1}", "StrackTrace:", exception.StackTrace);
            Error(errorMessage.ToString());
        }
        public virtual void Error(string message)
        {
            if (_logLevel >= Level.Error)
            {
                var errorMessage = new StringBuilder();
                errorMessage.AppendFormat("{0} => {1}", GetBaseMessage(_logname, "ERROR"), message);
                Write(errorMessage.ToString()); 
            }
        }
        public virtual void ErrorFormat(string format, params object[] args)
        {
            Error(String.Format(format, args));
        }
        private void Write(string message)
        {
            _logWriter.Write(new LogMsg(message));
            if(_logConfig.Console)
            {
                Console.WriteLine(message);
                System.Diagnostics.Debug.WriteLine(message);
            }
        }
    }
}
